'use strict';

var Transform = require('stream').Transform;
var inherits = require('util').inherits;
var extend = require('./extend.js');

function DestroyableTransform(opts) {
    Transform.call(this, opts);
    this._destroyed = false;
}

inherits(DestroyableTransform, Transform);

DestroyableTransform.prototype.destroy = function (err) {
    if (this._destroyed) {
        return;
    }

    this._destroyed = true;

    var self = this;
    process.nextTick(function () {
        if (err) {
            self.emit('error', err);
        }
        self.emit('close');
    });
};

// a noop _transform function
function noop(chunk, enc, callback) {
    callback(null, chunk);
}


// create a new export function, used by both the main export and
// the .ctor export, contains common logic for dealing with arguments
function through2(construct) {
    return function (options, transform, flush) {
        if (typeof options == 'function') {
            flush = transform;
            transform = options;
            options = {};
        }

        if (typeof transform != 'function') {
            transform = noop;
        }

        if (typeof flush != 'function') {
            flush = null;
        }

        return construct(options, transform, flush);
    };
}


// main export, just make me a transform stream!
module.exports = through2(function (options, transform, flush) {
    var t2 = new DestroyableTransform(options);

    t2._transform = transform;

    if (flush) {
        t2._flush = flush;
    }

    return t2;
});


// make me a reusable prototype that I can `new`, or implicitly `new`
// with a constructor call
module.exports.ctor = through2(function (options, transform, flush) {
    function Through2(override) {
        if (!(this instanceof Through2)) {
            return new Through2(override);
        }

        this.options = extend(options, override);

        DestroyableTransform.call(this, this.options);
    }

    inherits(Through2, DestroyableTransform);

    Through2.prototype._transform = transform;

    if (flush) {
        Through2.prototype._flush = flush;
    }

    return Through2;
});


module.exports.obj = through2(function (options, transform, flush) {
    var t2 = new DestroyableTransform(extend({ objectMode: true, highWaterMark: 16 }, options));

    t2._transform = transform;

    if (flush) {
        t2._flush = flush;
    }

    return t2;
});
